home *** CD-ROM | disk | FTP | other *** search
- /*
- * ptrace.c
- * By Ross Biro 1/23/92
- * edited by Linus Torvalds
- *
- * This file is subject to the terms and conditions of the GNU General Public
- * License. See the file README.legal in the main directory of this archive
- * for more details.
- */
-
- #include <linux/head.h>
- #include <linux/kernel.h>
- #include <linux/sched.h>
- #include <linux/mm.h>
- #include <linux/errno.h>
- #include <linux/ptrace.h>
- #include <linux/user.h>
-
- #include <asm/segment.h>
- #include <asm/system.h>
- #include <linux/debugreg.h>
-
- /*
- * does not yet catch signals sent when the child dies.
- * in exit.c or in signal.c.
- */
-
- /* change a pid into a task struct. */
- static inline struct task_struct * get_task(int pid)
- {
- int i;
-
- for (i = 1; i < NR_TASKS; i++) {
- if (task[i] != NULL && (task[i]->pid == pid))
- return task[i];
- }
- return NULL;
- }
-
- /*
- * this routine will get a word off of the processes priviledged stack.
- * the offset is how far from the base addr as stored in the TSS.
- * this routine assumes that all the priviledged stacks are in our
- * data space.
- */
- int ptrace_get_stack_long(struct task_struct *task, int offset)
- {
- unsigned char *stack;
-
- stack = (unsigned char *)task->tss.esp0;
- stack += offset;
- return (*((int *)stack));
- }
-
- /*
- * this routine will put a word on the processes priviledged stack.
- * the offset is how far from the base addr as stored in the TSS.
- * this routine assumes that all the priviledged stacks are in our
- * data space.
- */
- int ptrace_put_stack_long(struct task_struct *task, int offset,
- unsigned long data)
- {
- unsigned char * stack;
-
- stack = (unsigned char *) task->tss.esp0;
- stack += offset;
- *(unsigned long *) stack = data;
- return 0;
- }
-
- asmlinkage int sys_ptrace(long request, long pid, long addr, long data)
- {
- struct task_struct *child;
-
- if (request == PTRACE_TRACEME) {
- /* are we already being traced? */
- if (current->flags & PF_PTRACED)
- return -EPERM;
- /* set the ptrace bit in the proccess flags. */
- current->flags |= PF_PTRACED;
- return 0;
- }
- if (pid == 1) /* you may not mess with init */
- return -EPERM;
- if (!(child = get_task(pid)))
- return -ESRCH;
- if (request == PTRACE_ATTACH) {
- if (child == current)
- return -EPERM;
- if ((!child->dumpable || (current->uid != child->euid) ||
- (current->gid != child->egid)) && !suser())
- return -EPERM;
- /* the same process cannot be attached many times */
- if (child->flags & PF_PTRACED)
- return -EPERM;
- child->flags |= PF_PTRACED;
- if (child->p_pptr != current) {
- REMOVE_LINKS(child);
- child->p_pptr = current;
- SET_LINKS(child);
- }
- send_sig(SIGSTOP, child, 1);
- return 0;
- }
- if (!(child->flags & PF_PTRACED))
- return -ESRCH;
- if (child->state != TASK_STOPPED) {
- if (request != PTRACE_KILL && request != PTRACE_DETACH)
- return -ESRCH;
- }
- if (child->p_pptr != current)
- return -ESRCH;
-
- switch (request) {
- /* when I and D space are seperate, these will need to be fixed. */
- case PTRACE_PEEKTEXT: /* read word at location addr. */
- case PTRACE_PEEKDATA: {
- unsigned long tmp;
- int res;
-
- res = ptrace_read_long(child, addr, &tmp);
- if (res < 0)
- return res;
- res = verify_area(VERIFY_WRITE, (void *) data, sizeof(long));
- if (!res)
- put_fs_long(tmp,(unsigned long *) data);
- return res;
- }
-
- /* read the word at location addr in the USER area. */
- case PTRACE_PEEKUSR:
- return ptrace_peekusr (child, addr, data);
-
- /* when I and D space are seperate, this will have to be fixed. */
- case PTRACE_POKETEXT: /* write the word at location addr. */
- case PTRACE_POKEDATA:
- return ptrace_write_long(child,addr,data);
-
- case PTRACE_POKEUSR: /* write the word at location addr in the USER area */
- return ptrace_pokeusr (child, addr, data);
-
- case PTRACE_SYSCALL: /* continue and stop at next (return from) syscall */
- case PTRACE_CONT: { /* restart after signal. */
- if ((unsigned long) data > NSIG)
- return -EIO;
- if (request == PTRACE_SYSCALL)
- child->flags |= PF_TRACESYS;
- else
- child->flags &= ~PF_TRACESYS;
- child->exit_code = data;
- child->state = TASK_RUNNING;
- /* make sure the single step bit is not set. */
- ptrace_clrsingle(child);
- return 0;
- }
-
- /*
- * make the child exit. Best I can do is send it a sigkill.
- * perhaps it should be put in the status that it want's to
- * exit.
- */
- case PTRACE_KILL: {
- child->state = TASK_RUNNING;
- child->exit_code = SIGKILL;
- /* make sure the single step bit is not set. */
- ptrace_clrsingle (child);
- return 0;
- }
-
- case PTRACE_SINGLESTEP: { /* set the trap flag. */
- if ((unsigned long) data > NSIG)
- return -EIO;
- child->flags &= ~PF_TRACESYS;
- ptrace_setsingle (child);
- child->state = TASK_RUNNING;
- child->exit_code = data;
- /* give it a chance to run. */
- return 0;
- }
-
- case PTRACE_DETACH: { /* detach a process that was attached. */
- if ((unsigned long) data > NSIG)
- return -EIO;
- child->flags &= ~(PF_PTRACED|PF_TRACESYS);
- child->state = TASK_RUNNING;
- child->exit_code = data;
- REMOVE_LINKS(child);
- child->p_pptr = child->p_opptr;
- SET_LINKS(child);
- /* make sure the single step bit is not set. */
- ptrace_clrsingle(child);
- return 0;
- }
-
- default:
- return -EIO;
- }
- return -EIO;
- }
-
-
- asmlinkage void syscall_trace(void)
- {
- if ((current->flags & (PF_PTRACED|PF_TRACESYS))
- != (PF_PTRACED|PF_TRACESYS))
- return;
- current->exit_code = SIGTRAP;
- current->state = TASK_STOPPED;
- notify_parent(current);
- schedule();
- /*
- * this isn't the same as continuing with a signal, but it will do
- * for normal use. strace only continues with a signal if the
- * stopping signal is not SIGTRAP. -brl
- */
- if (current->exit_code)
- current->signal |= (1 << (current->exit_code - 1));
- current->exit_code = 0;
- return;
- }
-